<style>
/* 隐藏所有滚动条 */
*::-webkit-scrollbar {
  display: none;
}
.select-able *{cursor: default !important;}
.select-able .page-block{min-height: 30px;}
.select-able .page-block.selected{position: relative;}
.select-able .page-block.selected::before{content: '';position: absolute;left: 0;top: 0;width: 100%;height: 100%;z-index: 999;background: var(--color-primary);opacity: .4;}
</style>
<style lang="scss" scoped>
.page{user-select: none;width: 100%;height: 100%;overflow: auto;}
.empty{padding-bottom: 300px;}

.menu-popup{padding: 4px;border-radius: 8px;background: var(--color-bg-0);overflow: hidden;}
.custom-context-menu {box-shadow: var(--color-primary-r);}
.menu-item{width: 100px;padding: 6px 10px;border-radius: 6px;}
.menu-item:hover{background-color: var(--color-bg-1);}

</style>
<template>
  <div class="page" :class="{'select-able':selectAble}" @click="menuVisible=false;">
    <PageBlock class="page-block" v-for="row in page_back_list" v-bind="row" :info="info"></PageBlock>

    <div class="empty"></div>
    <!-- 使用 Vant Popup 作为右键菜单容器 -->
    <van-popup class="menu-popup" v-model:show="menuVisible" position="top-left" :style="{ top: `${menuPosition.y}px`, left: `${menuPosition.x}px` }" duration="0" :overlay="false" >
      <div>
        <p class="menu-item" v-for="item in menuItems" :title="item.tip" @click.stop="handleMenuClick(item)">{{item.title}}</p>
      </div>
    </van-popup>
  </div>
</template>

<script>

import PageBlock from "@/components/auto_import/PageBlock.vue";
import {getComponentConfigMap} from "@/components/auto_import/PageBlock.vue";
import s94Mjs from "s94-js";
import {msg} from "@/js/vant_tool.js";
import {json_encode} from "@/js/s94_tool.js";

/**用于配置页面时渲染用，主要包含了和父级页面的postMessage交互
 * @prop {*}
 */
export default {
  components: {PageBlock},
  props:{},
  data(){
    return {
      info: {},
      page_back_list: [],

      selectAble: true,

      componentConfigMap: {},
      selectPageBlock: null,
      callback_key: 0,

      //右键菜单
      menuVisible: false,
      menuPosition: {x:0,y:0},
      menuItems: [],
      copyPageBlock: null,
    }
  },
  methods:{
    postMessage(type, data){
      data = JSON.parse(JSON.stringify(data));
      parent.postMessage({type, data}, '*');
    },
    /**获取指定位置的板块ID
     * @param x
     * @param y
     * @returns {*|null}
     */
    getIdOfPosition(x, y) {
      // 获取指定坐标处的元素
      const elementAtPoint = document.elementFromPoint(x, y)
      if (!elementAtPoint) return null

      // 向上查找最近的page-block元素
      const blockElement = elementAtPoint.closest('[page_block_id]')
      if (!blockElement) return null

      let id = blockElement.getAttribute('page_block_id');
      return Number(id) || null;
    },
    /**根据ID，从板块列表中查找板块数据
     * @param page_block_id
     * @param fromList
     * @returns {{list: *[], index: number, row: null}}
     */
    findPageBlock(page_block_id, fromList){
      fromList = fromList || this.page_back_list;
      let res = {
        parent: null,
        list: fromList,
        index: -1,
        row: null,
      }
      if (!page_block_id) return res;
      function loop(parent, list){
        let index = -1;
        let find = list.find((row,i)=>{
          if (row.page_block_id === page_block_id){
            index = i;
            return true;
          }
        });
        if (find) {
          res.parent = parent;
          res.list = list;
          res.row = find;
          res.index = index;
        }else {
          list.forEach(row=>{
            if (!row.children || !row.children.length) return;
            loop(row, row.children);
          })
        }
      }
      loop(res.parent, res.list);
      return res;
    },
    /**判定ID是否在指定板块内，包括其子版块
     * @param page_block_id
     * @param row
     * @returns {boolean}
     */
    inPageBlock(page_block_id, row){
      return page_block_id===row.page_block_id || !!(row.children && this.findPageBlock(page_block_id, row.children).row);
    },
    /**设置选中的板块
     * @param row
     */
    setSelectBlockRow(row){
      if (this.selectPageBlock) delete this.selectPageBlock.class;
      if (row) {
        row.class = 'selected';
      }
      this.selectPageBlock = row || null;
      this.postMessage('select', this.selectPageBlock);
    },
    /**点击时选中板块判定
     * @param e
     */
    clickSelectBlock(e){
      if (!this.selectAble || this.menuVisible) return ;
      e.stopPropagation();

      let x = e.clientX;
      let y = e.clientY;
      let clickId = this.getIdOfPosition(x, y);
      if (!clickId) return this.setSelectBlockRow();
      if (this.selectPageBlock && clickId===this.selectPageBlock.page_block_id) {
        //点击选中的板块时，查看是否有父级板块，如果有，选中父级板块
        let find = this.findPageBlock(clickId);
        this.setSelectBlockRow(find.parent);
      }else {
        //点击板块不是选中板块时，选中对应板块
        this.setSelectBlockRow(this.findPageBlock(clickId).row);
      }
    },
    init({info, config}){
      this.info = info;
      let index = Date.now();
      s94Mjs.eachloop(config, 'children', (row, i, list)=>{
        row.page_block_id = index++;
        return true;
      })
      this.page_back_list = config;
      this.body_class();
    },
    /**添加板块
     * @param x
     * @param y
     * @param type
     * @returns {{$: ComponentInternalInstance, $data: {}, $props: Prettify<{message: Numeric}> & {}, $attrs: Data, $refs: Data, $slots: UnwrapSlotsType<{}>, $root: ComponentPublicInstance | null, $parent: ComponentPublicInstance | null, $host: Element | null, $emit: EmitFn<{}>, $el: any, $options: ComponentOptionsBase<any, any, any, any, any, any, any, any, any> & MergedComponentOptionsOverride, $forceUpdate: () => void, $nextTick: typeof nextTick, $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends ((...args: any) => infer R) ? ((...args: [R, R, OnCleanup]) => any) : ((...args: [any, any, OnCleanup]) => any), options?: WatchOptions): WatchStopHandle} & Readonly<{}> & Omit<{message: Numeric}, "close" | "open"> & ShallowUnwrapRef<{close: () => void, open: (props: Record<string, any>) => void}> & {} extends Ref ? {} : UnwrapRefSimple<{}> & ExtractComputedReturns<{}> & {} & ComponentCustomProperties & InjectToObject<{}>}
     */
    add({x, y, type}){
      this.addPageBlock({x,y}, {type});
    },
    addPageBlock(position, newPageBlock){
      newPageBlock = Array.isArray(newPageBlock) ? newPageBlock : [newPageBlock];
      let add_page_block_id = Date.now();
      // 递归补齐ID,默认值和判断type
      s94Mjs.eachloop(newPageBlock, 'children', (row)=>{
        row.page_block_id = add_page_block_id++;
        let typeConfig = this.componentConfigMap[row.type];
        if (!typeConfig) throw new Error(`组件类型不存在：${newPageBlock.type}`);
        if (!row.props){
          // 对于添加板块没有参数的情况，计算默认值
          row.props = {}
          Object.values(typeConfig.prop_map).forEach(param_row=>{
            //组件参数默认值
            row.props[param_row.name] = param_row.default || ({'array':'[]','object':'{}'}[param_row.type]) || '';
          })
        }
        if (!row.children && typeConfig.has_children){
          row.children = [];
        }
        return true;
      })

      // 确定添加位置
      let page_block_id = this.getIdOfPosition(position.x, position.y);
      let list = this.page_back_list;
      let index = undefined;
      if (page_block_id){
        let addChild = false;
        //有选中板块，并且板块可有子板块的情况
        if (this.selectPageBlock && this.selectPageBlock.children){
          // 判断是否拖到了选中板块内，是就表示添加子板块
          addChild = this.inPageBlock(page_block_id, this.selectPageBlock);
          if (addChild){ //如果添加子板块，需要修改添加列表为选中板块的子版块列表
            list = this.selectPageBlock.children;
            index = -1;
          }
        }
      }
      //确定插入位置
      if (typeof index==='undefined'){
        this.page_back_list.some((row,i)=>{
          if (this.inPageBlock(page_block_id, row)){
            index = i;
            return true;
          }
        })
      }
      if (typeof index==='undefined' || index===-1) index = list.length;
      newPageBlock.forEach(row=>{
        list.splice(index, 0, row);
        index++;
      })
      this.setSelectBlockRow(newPageBlock[0]);
      this.changeAfter();
    },
    update({page_block_id, props}){
      let res = this.findPageBlock(page_block_id);
      if (res.row) {
        res.row.props = props;
        this.changeAfter();
      }
    },
    delete({page_block_id}){
      let res = this.findPageBlock(page_block_id);
      if (res.index !== -1) {
        res.list.splice(res.index, 1);
        this.changeAfter();
      }
    },
    changeAfter(){
      this.postMessage('changeAfter', this.page_back_list);
    },
    body_class(){
      let class_arr = document.body.className.split(' ');
      let i = class_arr.indexOf('select-able');
      if (this.selectAble){
        if (i===-1) class_arr.push('select-able');
      }else {
        if (i!==-1) class_arr.splice(i,1);
      }
      document.body.className = class_arr.join(' ');
    },
    clickRight(e){
      // 需要自定义右键菜单的情况：
      let menuItems = [];
      if (this.copyPageBlock){
        menuItems.push({title:'粘贴', tip:'把复制的板块数据添加到此处(右键点击坐标)', action:'paste'});
      }
      let x = e.clientX;
      let y = e.clientY;
      if (this.selectPageBlock){
        let clickId = this.getIdOfPosition(x, y);
        if (clickId===this.selectPageBlock.page_block_id){
          menuItems.push({title:'复制一层', tip:'仅复制选中层数据', action:'copy'});
          menuItems.push({title:'复制所有', tip:'复制选中层以及子板块数据', action:'copy_all'});
        }
      }
      if (!menuItems.length) return;
      //自定义右键菜单
      e.preventDefault();
      e.stopPropagation();

      this.menuPosition = { x, y };
      this.menuItems = menuItems;
      this.menuVisible = true;
    },
    handleMenuClick(item){
      console.log('选择了:', item.title);
      let copyPageBlock;
      switch (item.action) {
        case 'copy':case 'copy_all': {
          copyPageBlock = JSON.parse(JSON.stringify(this.selectPageBlock));
          if (item.action==='copy' && copyPageBlock.children) copyPageBlock.children = [];
          // 处理复制的数据，去除page_block_id和class参数
          s94Mjs.eachloop(copyPageBlock, 'children', (row)=>{
            delete row.page_block_id;
            delete row.class;
            return true;
          })
          this.copyPageBlock = copyPageBlock;
        }break;
        case 'paste':{
          copyPageBlock = JSON.parse(JSON.stringify(this.copyPageBlock)); //拷贝对象
          this.addPageBlock(this.menuPosition, copyPageBlock);
        }break;
      }

      this.menuVisible = false;
    },
  },
  watch: {
    selectAble(v){
      this.body_class();
    }
  },
  computed:{
  },
  mounted(){
    // 给父页面发送组件Map
    getComponentConfigMap().then(componentConfigMap=>{
      this.componentConfigMap = componentConfigMap;
      this.postMessage('componentConfigMap', componentConfigMap);
    })

    let onMessage = (event)=>{
      // 建议检查来源
      // if (event.origin !== 'https://trusted-origin.com') return;
      let {type, data} = event.data;
      console.log(`--------H5收到${type}消息-------`)
      switch (type){
        case 'init': this.init(data); break;
        case 'add': this.add(data); break;
        case 'update': this.update(data); break;
        case 'delete': this.delete(data); break;
        case 'selectAble': {
          this.selectAble = !!data;
        } break;
      }
    };
    window.addEventListener('message', onMessage);
    this.onMessage = onMessage;

    window.addEventListener('click', this.clickSelectBlock, {capture:true})
    window.addEventListener('contextmenu', this.clickRight,{capture:true});
  },
  unmounted() {
    this.onMessage && window.removeEventListener('message', this.onMessage);

    window.removeEventListener('click', this.clickSelectBlock, {capture:true})
    window.removeEventListener('contextmenu', this.clickRight, {capture:true})
  }
}
</script>